package com.xe.gae.control;

import java.lang.reflect.Field;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Transaction;
import com.google.apphosting.api.DatastorePb.BeginTransactionRequest;

import com.xe.gae.model.XEResource;
import com.xe.util.XEReflex;
import com.xe.util.XEntity;
import com.xe.util.XFK;
import com.xe.util.XID;


public class XEDataStorageService<T> {


	DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
	XEReflex reflex = new XEReflex();

	Transaction transaction; 
	
	public void beginTransaction(){
		transaction = datastore.beginTransaction();
	}

	public void commit(){
		transaction.commit();
		transaction = null;
	}

	public void rollback(){
		transaction.rollback();
		transaction = null;
	}

	
	
	public void delete (String encoded){
				Key key=KeyFactory.stringToKey(encoded);
				this.beginTransaction();
				try {
					this.datastore.delete(key);	
				} catch (Exception e) {
					e.printStackTrace();
				}
				
				this.commit();
	}
	

	
	public T findByEncodeKey(Class c, String encoded){
		if (encoded==null) return null;
		Key key=KeyFactory.stringToKey(encoded);
		return this.findByKey(c, key);
	}
	
	public T findByKey(Key parent,Class c, String skey){
		String cname=c.getSimpleName();
		Key key = KeyFactory.createKey(parent,cname, skey);
		return this.findByKey(c, key);
	}

	
	public T findByKey(Class c, String skey){
		String cname=c.getSimpleName();
		Key key = KeyFactory.createKey(cname, skey);
		return this.findByKey(c, key);
	}
 
	public T findByKey(Class c, Key key){
		Entity e = this.get(key);
		if (e==null) return null;
		T o=null;
		try {
				o = (T) c.newInstance();
				for (Field f:c.getDeclaredFields()){
					String att   =f.getName();
					Object value = e.getProperty(att);
					if(value instanceof Key ){
						value = this.lazy(f.getType(),(Key) value);
						reflex.executeSetter(o,att,value );
					}
					else{
						reflex.executeSetter(o,att,value );
					}
				}
				((XEntity) o).setKey(e.getKey());
			} catch (InstantiationException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			} catch (IllegalAccessException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			return o;
			
	}
	
	public T loadByKey(Object o, Key key){
		Entity e = this.get(key);
		return loadByKey(o, e);
	}
	
	public Object lazy (Class c, Key key){
		Object o=null;
		try {
			o = (XEntity) c.newInstance();
		((XEntity)o).setKey(key);
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return o;
	}
	
	public T loadByKey(Object o, Entity e){
		
		try {
				Class c = o.getClass();
				for (Field f:c.getDeclaredFields()){
					String att   =f.getName();
					Object value = e.getProperty(att);
					if(value instanceof Key ){
						value = this.lazy(f.getType(),(Key) value);
						reflex.executeSetter(o,att,value );
					}
					else{
						reflex.executeSetter(o,att,value );
					}
				}
				((XEntity) o).setKey(e.getKey());
			} catch (Exception e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			return (T) o;
			
	}
	
	public Object getIdValue(Object o){
		Class c = o.getClass();
		String cname= c.getSimpleName();
		Entity e = new Entity(cname);
		
		for (Field f:c.getDeclaredFields()){
			XID id=f.getAnnotation(XID.class);
			if (id!=null){
				return reflex.executeGetter(o,f.getName());
			}
			
		}
		return null;
	} 
	
	public Key getEntityGroupKey(T object){
		Class c = object.getClass();
		String cname= c.getSimpleName();
		Object id = this.getIdValue(object);

		for (Field f:c.getDeclaredFields()){
			String att   =f.getName(); 
			XFK fk=f.getAnnotation(XFK.class);
			if (fk!=null && fk.isAncenstor()){
			XEntity xEntity = (XEntity) reflex.executeGetter(object,att );
			if(xEntity!=null){
				Key key = xEntity.getKey();
				return key;
			}
	  	  }
		}
		return null;
	}		
	
	public T put(T object){
		Class c = object.getClass();
		String cname= c.getSimpleName();
		Object id = this.getIdValue(object);
		Key egkey= getEntityGroupKey(object);
		
		Entity e ;
		if (egkey==null){
			if (id instanceof Long)  e= new Entity(cname,(Long) id);
			else e = new Entity(cname,id.toString());
		}
		else{
			if (id instanceof Long)  e= new Entity(cname,(Long) id,egkey);
			else e = new Entity(cname,id.toString(),egkey);
		}
		//Key EGkey=null;
		for (Field f:c.getDeclaredFields()){
			String att   =f.getName(); 
			XFK fk=f.getAnnotation(XFK.class);
			
			if (fk!=null){
				XEntity xEntity = (XEntity) reflex.executeGetter(object,att );
				if(xEntity!=null){
					Key key = xEntity.getKey();
					e.setProperty(att, key);
				}
			}
			else {
				Object value = reflex.executeGetter(object,att );
				e.setProperty(att, value);
			}
		}
		
		Key k = this.datastore.put(e);
		XEntity xe = (XEntity) object;
		xe.setKey(k);
		return object;
		
	}

	public Entity get(Key key){
		try {
			return  datastore.get(key);
		} catch (EntityNotFoundException e) {
			return null;
		}
		
	}

	public DatastoreService getDatastore() {
		return datastore;
	}

	public void setDatastore(DatastoreService datastore) {
		this.datastore = datastore;
	}

	public XEReflex getReflex() {
		return reflex;
	}

	public void setReflex(XEReflex reflex) {
		this.reflex = reflex;
	}

	
}
